package order

import (
	"fmt"
	"github.com/google/uuid"
	"github.com/shopspring/decimal"
	"gorm.io/gorm"
	"shop/constant"
	"shop/database/mysql"
	add1 "shop/internal/models/address"
	coupon1 "shop/internal/models/coupon"
	model "shop/internal/models/goods"
	order1 "shop/internal/models/order"
	"shop/pkg"
	"sync"
)

type CreateOrderReq struct {
	AddressID uint `json:"address_id"`
	Coupon    uint `json:"coupon"`
	Goods     []struct {
		SkuID uint `json:"sku_id"`
		Num   uint `json:"num"`
	} `json:"goods"`
	PayType uint   `json:"pay_type"`
	Node    string `json:"node"`
}

func getAmount(in *CreateOrderReq, skuMap map[uint]*model.GoodsSku) (decimal.Decimal, error) {
	amount := decimal.NewFromInt(0)
	for _, val := range in.Goods {
		//通过skuID 查询sku表数据price，vip_price
		skuInfo := skuMap[val.SkuID]
		//转化为string类型方便计算
		price, err := decimal.NewFromString(skuInfo.Price)
		if err != nil {
			return decimal.Decimal{}, err
		}
		//将数量转换为int类型
		num := decimal.NewFromInt(int64(val.Num))
		//计算出订单总价格
		amount = amount.Add(price.Mul(num))
	}
	return amount, nil
}

func getDiscountAmount(amount decimal.Decimal, couponInfo *coupon1.Coupon) (decimal.Decimal, error) {
	ninAmount, err := decimal.NewFromString(couponInfo.MinAmount)
	if err != nil {
		return decimal.Decimal{}, err
	}
	//用于比较两个值的大小
	if amount.LessThan(ninAmount) {
		//如果 amount 小于 ninAmount，则返回 true，否则返回 false
		// decimal.NewFromInt(0)表示折扣金额为 0（即不应用任何折扣
		return decimal.NewFromInt(0), nil
	}
	discountValue, err := decimal.NewFromString(couponInfo.Discount)
	if err != nil {
		return decimal.Decimal{}, err
	}

	switch couponInfo.DiscountType {
	case constant.CouponDiscountTypeFixed:
		return discountValue, nil
	case constant.CouponStateExpired:
		//计算折扣价格
		return amount.Sub(amount.Mul(discountValue)), nil
	default:
		return decimal.NewFromInt(0), nil
	}
}

func getCoupon(userID, id uint) (*coupon1.Coupon, error) {
	userCouponMod := new(coupon1.Coupon)
	userCouponInfo, err := userCouponMod.CouponInfoByCouponID(int64(id))
	if err != nil {
		return nil, err
	}
	if uint(userCouponInfo.UserID) != userID {
		return nil, fmt.Errorf("优惠券权限异常")
	}
	switch userCouponInfo.CouponStatus {
	case 2:
		return nil, fmt.Errorf("优惠券已使用")
	case 3:
		return nil, fmt.Errorf("优惠券已过期")
	}

	couponMod := new(coupon1.Coupon)
	return couponMod.CouponInfoByCouponID(int64(id))
}

func getAddress(userID, id uint) (*add1.Address, error) {
	addMod := new(add1.Address)
	addressInfo, err := addMod.AddressInfoByID(int(id))
	if err != nil {
		return nil, err
	}

	if addressInfo.UserID != int(userID) {
		return nil, fmt.Errorf("地址权限异常")
	}
	return addressInfo, nil
}

// 创建订单
func CreateOrder(userID uint, in *CreateOrderReq) (string, error) {
	//等待组
	wg := sync.WaitGroup{}

	var skuMap map[uint]*model.GoodsSku
	var goodMap map[uint]*model.Goods
	var addressInfo *add1.Address
	var couponInfo *coupon1.Coupon
	var err error

	//开启3个携程
	wg.Add(2)
	go func() {
		//获取商品信息，包括SKU信息
		skuMap, goodMap, err = getGoodInfos(in)
		wg.Done()
		//停止携程
	}()

	go func() {
		//获取地址信息
		addressInfo, err = getAddress(userID, in.AddressID)
		wg.Done()
	}()

	go func() {
		//获取优惠券信息
		couponInfo, err = getCoupon(userID, in.Coupon)
		wg.Done()
	}()

	//取消等待组
	wg.Wait()

	//计算总金额
	amount, err := getAmount(in, skuMap)
	if err != nil {
		return "", err
	}

	//计算优惠金额
	goodsTotalPrice, err := getDiscountAmount(amount, couponInfo)
	if err != nil {
		return "", err
	}

	fmt.Println(goodsTotalPrice)
	//生成订单编号
	no := uuid.NewString()

	//创建订单数据及修改库存，事务处理
	err = createOrder(userID, no, addressInfo, in, amount, amount, skuMap, goodMap)
	if err != nil {
		return "", err
	}

	newPayData := pkg.NewPayData()
	url, err := newPayData.Pay(&pkg.PayData{
		OutTradeNo: no,
		//TotalAmount: strconv.FormatFloat(goodsTotalPrice, 'f', 2, 64),
		Subject:   "订单支付",
		ReturnUrl: constant.Return_url,
		NotifyUrl: constant.Notify_url,
	})

	//获取支付链接
	return url, nil
}

func createOrder(
	userID uint,
	no string,
	addressInfo *add1.Address,
	in *CreateOrderReq,
	discountAmount decimal.Decimal,
	amount decimal.Decimal,
	skuMap map[uint]*model.GoodsSku,
	goodMap map[uint]*model.Goods,
) error {
	//通过事务和锁机制来保证订单创建和库存更新的正确性和一致性，适用于高并发的电商场景。
	return mysql.WithMysql(func(db *gorm.DB) error {
		return db.Transaction(func(tx *gorm.DB) error {
			orderMod := order1.Order{
				UserId:        int(userID),
				OrderNo:       no,
				Name:          addressInfo.Recipient,
				Phone:         addressInfo.Phone,
				Address:       addressInfo.AddressLine,
				Note:          in.Node,
				CouponId:      int(in.Coupon),
				Favorable:     discountAmount.StringFixed(2),
				Amount:        amount.StringFixed(2),
				PayType:       int(in.PayType),
				CommentStatus: false,
			}

			var up []*model.UpdateStock
			var orderGodos []*order1.OrderGoods
			//goland:noinspection LanguageDetectionInspection
			for _, val := range in.Goods {
				skuInfo := skuMap[val.SkuID]
				goodInfo := goodMap[uint(skuInfo.GoodsId)]
				orderGodos = append(orderGodos, &order1.OrderGoods{
					GoodsId:  int(goodInfo.ID),
					Title:    goodInfo.Title,
					Img:      goodInfo.Img,
					SkuId:    int(skuInfo.ID),
					Quantity: int(val.Num),
					Price:    skuInfo.Price,
				})

				up = append(up, &model.UpdateStock{
					SkuID: val.SkuID,
					Num:   val.Num,
				})
			}

			err := orderMod.AddOrder(orderGodos)
			if err != nil {
				return err
			}

			skuMod := new(model.GoodsSku)
			return skuMod.UpdateStock(up)
		})
	})
}

func getGoodInfos(in *CreateOrderReq) (map[uint]*model.GoodsSku, map[uint]*model.Goods, error) {
	var skuIDs []uint
	for _, val := range in.Goods {
		skuIDs = append(skuIDs, val.SkuID)
	}

	skuMod := new(model.GoodsSku)
	//查询出sku表中的skuID列表数据
	skuInfos, err := skuMod.SearchSkuIDs(skuIDs)
	if err != nil {
		return nil, nil, err
	}
	if len(skuIDs) != len(skuInfos) {
		return nil, nil, fmt.Errorf("商品不存在")
	}

	var goodIDs []uint
	skuMap := make(map[uint]*model.GoodsSku)
	for _, val := range skuInfos {
		skuMap[val.ID] = val
		goodIDs = append(goodIDs, uint(val.GoodsId))
	}

	goodMod := new(model.Goods)
	goodInfos, err := goodMod.GoodsListByIds(goodIDs)
	if err != nil {
		return nil, nil, err
	}

	goodMap := make(map[uint]*model.Goods)
	for _, val := range goodInfos {
		goodMap[val.ID] = val
	}

	/*	 skuIDs = append(skuIDs, val.SkuID)
	作用:
		将当前遍历到的 val.SkuID 添加到 skuIDs 切片中。
		skuIDs 是一个 []uint 类型的切片，用于存储所有商品的 SKU ID。
	示例:
		第一次遍历时，skuIDs 变为 [101]。
		第二次遍历时，skuIDs 变为 [101, 102]。
		第三次遍历时，skuIDs 变为 [101, 102, 103]
	*/

	return skuMap, goodMap, nil
}
